﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Chefbook.HelperMethods;
using Chefbook.DataAccessLayer;

namespace Chefbook.DataObjects
{
    /// <summary>
    /// Represents a category in a hierarchy structure that can contain items
    /// </summary>
    public class Category : IDataObject
    {
        /// <summary>
        /// Internal to allow cleanup without infinite loop.  Do not use.
        /// </summary>
        internal List<Category> subcategories = new List<Category>();
        /// <summary>
        /// Internal to allow cleanup without infinite loop.  Do not use.
        /// </summary>
        internal List<Recipe> recipes = new List<Recipe>();

        private ChefbookEntities db;
        private Category parent;
        private string name;
        private readonly int id;

        /// <summary>
        /// Constructor for a category with a given parent and category name
        /// </summary>
        /// <param name="parent">Category that this Category is contained in.  Null if root category</param>
        /// <param name="name">The name of the Category</param>
        public Category(Category parent)
        {
            Parent = parent;
            parent.PerformIfNotNull(() => parent.subcategories.AddIfNew(this));
            Name = string.Empty;
        }

        /// <summary>
        /// Gets or sets the name of the category
        /// </summary>
        public string Name
        {
            get { return name; }
            set { name = value.ThrowIfNull("Name"); }
        }

        public int Id
        {
            get { return id; }
        }

        /// <summary>
        /// Removes this folder from its parent
        /// </summary>
        public void Delete()
        {
            this.parent.PerformIfNotNull(() => this.parent.subcategories.Remove(this));
            List<Recipe> recipesToRemoveFrom = new List<Recipe>(recipes);
            foreach (var recipe in recipesToRemoveFrom)
            {
                recipe.RemoveCategory(this);
            }
        }

        /// <summary>
        /// Gets or sets the parent category
        /// </summary>
        public Category Parent
        {
            get { return parent; }
            set
            {
                parent.PerformIfNotNull(() => this.parent.subcategories.Remove(this));
                this.parent = value;
                value.PerformIfNotNull(() => value.subcategories.AddIfNew(this));
            }
        }

        /// <summary>
        /// Adds a recipe to this category's direct recipes
        /// </summary>
        /// <param name="recipeToAdd">The recipe to add</param>
        public void AddRecipe(Recipe recipeToAdd)
        {
            recipeToAdd.ThrowIfNull("recipeToAdd");
            recipes.AddIfNew(recipeToAdd);
            recipeToAdd.categories.AddIfNew(this);
        }

        /// <summary>
        /// Removes a recipe from this category
        /// </summary>
        /// <param name="recipe">The recipe to remove</param>
        public void RemoveRecipe(Recipe recipe)
        {
            recipe.ThrowIfNull("recipe");
            ParameterValidation.ValidateArgument(() => recipes.Contains(recipe), "recipe");
            recipes.Remove(recipe);
            recipe.RemoveCategory(this);
        }

        /// <summary>
        /// Gets a readonlylist of direct subcategories of this category
        /// </summary>
        public ReadOnlyCollection<Category> Subcategories
        {
            get { return new ReadOnlyCollection<Category>(subcategories); }
        }

        /// <summary>
        /// Gets a readonly list of recipes contained directly in this category 
        /// not one of its subcategories
        /// </summary>
        public ReadOnlyCollection<Recipe> DirectRecipes
        {
            get { return new ReadOnlyCollection<Recipe>(recipes); }
        }

        /// <summary>
        /// Gets readonly list of all recipes contained in this category or one of its subcategories
        /// </summary>
        public ReadOnlyCollection<Recipe> AllRecipes
        {
            get
            {
                List<Recipe> allRecipes = new List<Recipe>(recipes);
                foreach (var cat in subcategories)
                {
                    allRecipes.AddRange(cat.AllRecipes);
                }
                return new ReadOnlyCollection<Recipe>(allRecipes);
            }
        }

        public override string ToString()
        {
            return Name;
        }
    }
}